//
//  utils.c
//
//
//  Created by  AbbyCin on 11/1/14.
//  Copyright (c) 2014  AbbyCin. All rights reserved.
//

#include "utils.h"

char *do_parse(const char *buf)
{
    char *filepath = NULL;
    char *first_line = NULL;
	char *home = "./index.html";
    int i=0;
    int j=4;
    int len = 0;
    
    for(i = 0; buf[i+1] != '\n'; i++);
    
    first_line = (char *)malloc(i);
    
    strncpy(first_line, buf, i);
    
    len = i - 12;
    filepath = (char *)malloc(i+1);
	memset(filepath, 0, i+1);
	if(i <= 14)
	{
		strcpy(filepath, home);
		return filepath;
	}
	else
	{
		filepath[0] = '.';
		for(i = 1; i < len; i++)
			filepath[i] = first_line[j++];
	}
    free(first_line);
    return filepath;
}

void do_resolve(char filetype[], const char path[]) /* resolve http header */
{
    int i;
    char *minetype[]={
        "video/mp4",
        "audio/mp3",
        "image/jpeg", /* jpeg */
        "image/jpg", /* jpg */
        "image/png",
		"image/gif",
        "text/html",
        "text/plain"
    };

    bzero(filetype, MAX_SIZE);
    
    for(i=0; i< 8; i++)
        if(strstr(path, minetype[i]))
        {
            strcpy(filetype, minetype[i]);
            break;
        }
}


void *mymmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)
{
    void *ptr = NULL;
    
    if((ptr = mmap(addr, len, prot, flags, fd, offset)) == ((void *) -1))
    {
        syslog(LOG_CRIT,"mmap error: %s", strerror(errno));
        //exit(1);
    }
    
    return ptr;
}

ssize_t writen(int sockfd, void *filebuf, size_t n)     /* can NOT set to 'static' internal */
{
    size_t nleft = n;           /* number of left */
    ssize_t nwritten;           /* number of written */
    char *buf = filebuf;
    
    while(nleft > 0)
    {
        if((nwritten = write(sockfd, buf, nleft)) == -1)
        {
            if(errno == EINTR)
                nwritten = 0;
            else
                return -1;
        }
        
        nleft -= nwritten;
        buf += nwritten;        /* pointer's offset increase nwritten */
    }
    return n;
}

void sock_writen(int sockfd, void *filebuf, size_t n)
{
    if(writen(sockfd, filebuf, n) != n)
    {
        syslog(LOG_ERR, "write data to socket: %s", strerror(errno));
       // exit(1);
    }
}

void *do_trans(void *acfd)
{
	pthread_detach(pthread_self());			/* avoiding bloack main thread*/
    int fd;
	int sockfd;
    char header[MAX_SIZE];
    void *fd_cast = NULL;          /* mmap return */
    struct stat file_stat;
    char filetype[MAX_SIZE];
    char *filepath = NULL;
	char *path = NULL;
	
	sockfd = (int)acfd;
	
	path = (char *)malloc(MAX_SIZE);
	bzero(path, MAX_SIZE);
	
	read(sockfd, path, MAX_SIZE);		/* get what browser wants */
	
	filepath = (char *)malloc(256);
	memset(filepath, 0, 256);
    
    filepath = do_parse(path);
    
    fd = open(filepath, O_RDONLY);
    if(fd == -1)
    {
        syslog(LOG_ERR, "open failed: %s", strerror(errno));
        //exit(EXIT_FAILURE);
    }
    
    lstat(filepath, &file_stat);
    fd_cast = mymmap(0, file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);      /* cast file descriptor to memory */
    close(fd);
    
	if(strstr(filepath, "index.html"))
	{
		memset(filetype, 0, MAX_SIZE);
		strcpy(filetype, "text/html");
	}
	else
		do_resolve(filetype, filepath);
    
	free(filepath);
    /* server response header */
    sprintf(header,"HTTP/1.1 200 OK\r\n");
    sprintf(header,"%sServer: pisces for ever\r\n",header);
#ifdef __APPLE__                                                /* works on OS X */
    sprintf(header,"%sContent-length: %lld\r\n",header, file_stat.st_size);
#else                                                           /* tested on dragonfly bsd and archlinux */
    sprintf(header,"%sContent-length: %ld\r\n",header, file_stat.st_size);
#endif
    sprintf(header,"%sConnection: Keep-Alive\r\n",header);
    sprintf(header,"%sContent-type: %s\r\n\r\n",header,filetype);
    
    sock_writen(sockfd, header, strlen(header));
    
    /* write file contents; ie: send response body to client */
    sock_writen(sockfd, fd_cast, file_stat.st_size);
    close(sockfd);
    
    memset(header, 0, MAX_SIZE);                /* clean header */
    
    if(munmap(fd_cast, file_stat.st_size) == -1)
    {
        syslog(LOG_ERR, "un map memory failed: %s", strerror(errno));
        //exit(1);
    }
    pthread_exit(NULL);
}
